import { useEffect, useRef } from "react";
import {
  useStatus,
  useLayout,
  useSpeed,
  useStatusActions,
  useCheckCollision,
  Direction,
  Status,
} from "../../context";
import {
  isDirection,
  isHorizontalMove,
  isVerticalMove,
  isOutOfBounds,
} from "../../utils";

/** 蛇身生成 */
export function useSnakeBodyGenerate(
  direction: Direction,
  snakeHead: SnakeNode,
  snakeBody: SnakeNode[],
  setSnakeBody: SetSnakeBody,
  createSnakeNode: CreateSnakeNode
) {
  const status = useStatus();
  const { gridSize } = useLayout();
  const { isCollision } = useCheckCollision();

  useEffect(() => {
    if (status === Status.start && isCollision) {
      const newSnakeNode = createSnakeNode(
        snakeBody.length,
        snakeHead.x,
        snakeHead.y,
        gridSize,
        direction
      );
      const newSnakeBody = snakeBody.concat(newSnakeNode);
      setSnakeBody(newSnakeBody);
    }
  }, [status, isCollision]);
}

/** 蛇头移动 */
export function useSnakeHeadMove(
  direction: Direction,
  setSnakeHead: SetSnakeHead
) {
  const speed = useSpeed();
  const status = useStatus();
  const intervalRef = useRef(0);
  const { gridSize } = useLayout();

  // 更新蛇头位置
  const updatePoint = (key: string, callback: Function) => {
    setSnakeHead((data) => {
      return {
        ...data,
        direction,
        [key]: callback(key, data),
      };
    });
  };

  useEffect(() => {
    if (status === Status.start) {
      intervalRef.current = setInterval(() => {
        if (direction === Direction.right) {
          updatePoint("x", (key: string, data: any) => data[key] + gridSize);
        }
        if (direction === Direction.left) {
          updatePoint("x", (key: string, data: any) => data[key] - gridSize);
        }
        if (direction === Direction.down) {
          updatePoint("y", (key: string, data: any) => data[key] + gridSize);
        }
        if (direction === Direction.up) {
          updatePoint("y", (key: string, data: any) => data[key] - gridSize);
        }
      }, speed);
    } else {
      clearInterval(intervalRef.current);
    }

    return () => clearInterval(intervalRef.current);
  }, [status, direction, speed]);
}

/** 蛇身移动 */
export function useSnakeBodyMove(
  direction: Direction,
  snakeHead: SnakeNode,
  snakeBody: SnakeNode[],
  setSnakeBody: SetSnakeBody
) {
  const status = useStatus();
  const { stageSize } = useLayout();
  const { onReset } = useStatusActions();

  useEffect(() => {
    const { x, y } = snakeBody[0];
    const isVertical = isVerticalMove(direction);
    const isHorizontal = isHorizontalMove(direction);

    if (
      status !== Status.start ||
      (isHorizontal && snakeHead.x === x) ||
      (isVertical && snakeHead.y === y)
    ) {
      return;
    }
    if (isOutOfBounds(snakeHead.x, snakeHead.y, stageSize, stageSize)) {
      onReset();
      alert("游戏结束");
      window.location.reload();
      return;
    }
    const newSnakeBody = snakeBody.map((node, index, nodeList) => {
      if (index === 0) {
        return {
          ...node,
          x: snakeHead.x,
          y: snakeHead.y,
        };
      } else {
        return {
          ...node,
          x: nodeList[index - 1].x,
          y: nodeList[index - 1].y,
        };
      }
    });
    setSnakeBody(newSnakeBody);
  }, [status, snakeHead.x, snakeHead.y]);
}

/** 重置蛇 */
export function useResetSnake(
  setSnakeHead: SetSnakeHead,
  setSnakeBody: SetSnakeBody,
  createSnakeHead: () => SnakeNode
) {
  const status = useStatus();

  useEffect(() => {
    if (status === Status.reset) {
      const snakeHead = createSnakeHead();
      setSnakeHead(snakeHead);
      setSnakeBody([snakeHead]);
    }
  }, [status]);
}

/** 事件绑定 */
export function useEventBind(direction: Direction, setDirection: SetDirection) {
  const status = useStatus();

  useEffect(() => {
    if (status === Status.reset) {
      setDirection(Direction.right);
    }
    const keydownHandle = (event: KeyboardEvent) => {
      if (isDirection(direction, event.key)) {
        setDirection(event.key as Direction);
      }
    };
    document.addEventListener("keydown", keydownHandle);

    return () => document.removeEventListener("keydown", keydownHandle);
  }, [status, direction]);
}
